﻿using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Linq;
using System.Web.Routing;
using Orchard;
using Orchard.Core.Contents.Controllers;
using Orchard.DisplayManagement;
using Orchard.Environment.Configuration;
using Orchard.Environment.Descriptor.Models;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.VirtualPath;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Mvc.Extensions;
using Orchard.Themes.Services;
using Orchard.UI.Admin;
using Orchard.UI.Notify;
using Orchard.Widgets;
using Orchard.Widgets.Models;
using Orchard.Widgets.Services;
using Orchard.Environment.Extensions;
using So.WidgetThemeLayers.Models;

namespace So.WidgetThemeLayers.Controllers
{
    [ValidateInput(false), Admin]
    public class AdminController : Controller
    {
        private readonly IWidgetsService _widgetsService;
        private readonly ISiteThemeService _siteThemeService;
        private readonly IVirtualPathProvider _virtualPathProvider;
        private readonly IExtensionManager _extensionManager;
        private readonly ShellSettings _shellSettings;
        private readonly ShellDescriptor _shellDescriptor;

        public AdminController(
            IOrchardServices services,
            IWidgetsService widgetsService,
            IShapeFactory shapeFactory,
            ISiteThemeService siteThemeService,
            IVirtualPathProvider virtualPathProvider, IExtensionManager extensionManager, ShellSettings shellSettings, ShellDescriptor shellDescriptor)
        {

            Services = services;
            _widgetsService = widgetsService;
            _siteThemeService = siteThemeService;
            _virtualPathProvider = virtualPathProvider;
            _extensionManager = extensionManager;
            _shellSettings = shellSettings;
            _shellDescriptor = shellDescriptor;

            T = NullLocalizer.Instance;
            Logger = NullLogger.Instance;
            Shape = shapeFactory;
        }

        private IOrchardServices Services { get; set; }
        public Localizer T { get; set; }
        public ILogger Logger { get; set; }
        dynamic Shape { get; set; }

        public ActionResult Index(int? layerId, string themeId = "")
        {
            IEnumerable<LayerPart> layers = _widgetsService.GetLayers().ToList();

            if (!layers.Any())
            {
                Services.Notifier.Error(T("There are no widget layers defined. A layer will need to be added in order to add widgets to any part of the site."));
                return RedirectToAction("AddLayer");
            }

            LayerPart currentLayer = layerId == null
                ? layers.FirstOrDefault()
                : layers.FirstOrDefault(layer => layer.Id == layerId);

            if (currentLayer == null && layerId != null)
            { // Incorrect layer id passed
                Services.Notifier.Error(T("Layer not found: {0}", layerId));
                return RedirectToAction("Index");
            }

            ExtensionDescriptor currentTheme = _siteThemeService.GetSiteTheme();
            var themeExtensions = GetThemeExtensions();
            string layerRule = string.Empty;
            string currentThemeFilter = themeId;
            if (!string.IsNullOrWhiteSpace(currentThemeFilter))
            {
                layerRule = string.Format("theme(\"{0}\")", currentThemeFilter);
                //todo ...a real rule parse because this doesn't work in al cases
                string disabledForThemeRule = "not " + layerRule;
                layers = layers.Where(s => s.LayerRule.IndexOf(layerRule, StringComparison.InvariantCultureIgnoreCase) > -1 &&
                    s.LayerRule.IndexOf(disabledForThemeRule, StringComparison.InvariantCultureIgnoreCase) == -1);
                currentTheme = themeExtensions.FirstOrDefault(s => s.Id == currentThemeFilter);
            }

            IEnumerable<string> allZones = _widgetsService.GetZones();
            IEnumerable<string> currentThemesZones = _widgetsService.GetZones(currentTheme);

            string zonePreviewImagePath = string.Format("{0}/{1}/ThemeZonePreview.png", currentTheme.Location, currentTheme.Id);
            string zonePreviewImage = _virtualPathProvider.FileExists(zonePreviewImagePath) ? zonePreviewImagePath : null;

            dynamic viewModel = Shape.ViewModel()
                .CurrentTheme(currentTheme)
                .CurrentLayer(currentLayer)
                .CurrentThemeFilter(currentThemeFilter)
                .LayerRule(layerRule)
                .Themes(themeExtensions.Select(extensionDescriptor =>
                {
                    var themeEntry = new ThemeViewModel()
                    {
                        Id = extensionDescriptor.Id,
                        Name = extensionDescriptor.Name
                    };
                    return themeEntry;
                }))
                .Layers(layers)
                .Widgets(_widgetsService.GetWidgets())
                .Zones(currentThemesZones)
                .OrphanZones(allZones.Except(currentThemesZones))
                .OrphanWidgets(_widgetsService.GetOrphanedWidgets())
                .ZonePreviewImage(zonePreviewImage);

            // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
            return View((object)viewModel);
        }

        [HttpPost, ActionName("Index")]
        public ActionResult IndexWidgetPOST(int widgetId, string returnUrl, int? layerId, string moveUp, string moveDown, string moveHere, string moveOut)
        {
            if (!string.IsNullOrWhiteSpace(moveOut))
                return DeleteWidget(widgetId, returnUrl);

            if (!IsAuthorizedToManageWidgets())
                return new HttpUnauthorizedResult();

            if (!string.IsNullOrWhiteSpace(moveUp))
                _widgetsService.MoveWidgetUp(widgetId);
            else if (!string.IsNullOrWhiteSpace(moveDown))
                _widgetsService.MoveWidgetDown(widgetId);
            else if (!string.IsNullOrWhiteSpace(moveHere))
                _widgetsService.MoveWidgetToLayer(widgetId, layerId);

            return this.RedirectLocal(returnUrl, () => RedirectToAction("Index"));
        }

        private bool IsAuthorizedToManageWidgets()
        {
            return Services.Authorizer.Authorize(Permissions.ManageWidgets, T("Not authorized to manage widgets"));
        }

        [HttpPost, ActionName("EditWidget")]
        [FormValueRequired("submit.Delete")]
        public ActionResult EditWidgetDeletePOST(int id, string returnUrl)
        {
            return DeleteWidget(id, returnUrl);
        }

        private ActionResult DeleteWidget(int id, string returnUrl)
        {
            if (!IsAuthorizedToManageWidgets())
                return new HttpUnauthorizedResult();

            WidgetPart widgetPart = null;
            widgetPart = _widgetsService.GetWidget(id);
            if (widgetPart == null)
                return HttpNotFound();
            try
            {
                _widgetsService.DeleteWidget(widgetPart.Id);
                Services.Notifier.Information(T("Widget was successfully deleted"));
            }
            catch (Exception exception)
            {
                Logger.Error(T("Removing Widget failed: {0}", exception.Message).Text);
                Services.Notifier.Error(T("Removing Widget failed: {0}", exception.Message));
            }

            return this.RedirectLocal(returnUrl, () => RedirectToAction("Index"));
        }

        private IEnumerable<ExtensionDescriptor> GetThemeExtensions()
        {
            IEnumerable<ExtensionDescriptor> themes = _extensionManager.AvailableExtensions()
                .Where(extensionDescriptor =>
                {
                    bool hidden = false;
                    string tags = extensionDescriptor.Tags;
                    if (tags != null)
                    {
                        hidden = tags.Split(',').Any(t => t.Trim().Equals("hidden", StringComparison.OrdinalIgnoreCase));
                    }

                    // is the theme allowed for this tenant ?
                    bool allowed = _shellSettings.Themes.Length == 0 || _shellSettings.Themes.Contains(extensionDescriptor.Id);

                    return !hidden && allowed && DefaultExtensionTypes.IsTheme(extensionDescriptor.ExtensionType) &&
                            _shellDescriptor.Features.Any(sf => sf.Name == extensionDescriptor.Id);

                });
            return themes;
        }

        public ActionResult ChooseWidget(int layerId, string zone, string returnUrl)
        {
            return RedirectToRoute(new RouteValueDictionary() {
                {"Controller","Admin"},
                {"Area","Orchard.Widgets"},
                {"Action","ChooseWidget"},
                {"layerId",layerId},
                {"zone",zone},
                {"returnUrl",returnUrl}
            });
        }

        public ActionResult EditWidget(int id)
        {
            return RedirectToRoute(new RouteValueDictionary() {
                {"Controller","Admin"},
                {"Area","Orchard.Widgets"},
                {"Action","EditWidget"},
                {"id",id}
            });
        }

        //public ActionResult AddWidget(int layerId, string widgetType, string zone, string returnUrl) {
        //    return RedirectToRoute(new RouteValueDictionary() {
        //        {"Controller","Admin"},
        //        {"Area","Orchard.Widgets"},
        //        {"Action","AddWidget"},
        //        {"layerId",layerId},
        //        {"widgetType",widgetType},
        //        {"zone",zone},
        //        {"returnUrl",returnUrl}
        //    });
        //}

    }
}